/**
 * @Author Heinz Hölzer
 */

package scuts.macro.builder;

#if macro
import haxe.macro.Compiler;
import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Expr.Position;


typedef ED = ExprDef;
#end
/*
typedef Function = {
  var name : Null<String>;
  var args : Array<FunctionArg>;
  var ret : Null<ComplexType>;
  var expr : Expr;
}

typedef FunctionArg = {
  var name : String;
  var opt : Bool;
  var type : Null<ComplexType>;
  var value : Null<Expr>;
}
*/

class ShortLambda 
{

  
  
  public static function make(arr:Array<Expr>):Expr 
  {
    return 
      if (arr.length == 1) {
        
        makeOneArgument(arr[0], []);
      }
      else makeMultiArguments(arr);
  }
  
  public static function makeMultiArguments(arr:Array<Expr>):Expr {
    var args = [];
    
    for (i in 0...arr.length - 1) {
      var a = arr[i];
      switch (a.expr) {
        case ED.EConst(c): switch (c) {
          case CIdent(s):
            args.push( { name: s, opt:false, type:null, value:null } );
          case CString(s):
            var variable = varFromString(s, a.pos);
            args.push( { name: variable.name, opt:false, type:variable.type, value:null });
          default: throw "Unexpected";
        }
        
                
        default: throw "Unexpected";
      }
    }
    return makeOneArgument(arr[arr.length-1], args);
  }
  
  
  
  public static function makeOneArgument(expr:Expr, otherArgs:Array<FunctionArg>) {
    
    var p = expr.pos,
      shortCutLambda = function (expr) {
        var args = [{ name: "_", opt:false, type:null, value:null }],
          fun = { ret:null, expr: { expr:makeFunctionBody(expr), pos:p }, args:args, params:[] };
        return ED.EFunction(null, fun);
      },
      def = switch (expr.expr) 
      {
        case ED.EBinop(op, e1, e2):
          
          if (op == OpAssign) switch (e1.expr) 
            {
            case ED.EConst(c): switch (c) 
              {
              case CIdent(s):
                var args = otherArgs.concat([{ name: s, opt:false, type:null, value:null }]),
                  fun = { ret:null, expr: { expr:makeFunctionBody(e2), pos:p }, args:args, params:[] };
                ED.EFunction(null, fun);
              case CString(s):
                var variable = varFromString(s, e1.pos),
                  args = otherArgs.concat([{ name: variable.name , opt:false, type: variable.type, value:null }]),
                  fun = { ret:null, expr: { expr:makeFunctionBody(e2), pos:p }, args:args, params:[] };
                ED.EFunction(null, fun);
              default: throw "Ident Expected for " + e1;
              }
            case ED.EBinop(op, varName, varType):
              if (op == OpBoolOr) switch (varName.expr) 
              {
                case ED.EConst(c): switch (c) 
                {
                  case CIdent(s):
                    switch (varType.expr) {
                      case ED.EConst(c):
                        switch (c) {
                          case CIdent(u):
                          default: throw "Unexpected";
                        }
                      default: throw "Unexpected";
                      
                    }
                    var args = otherArgs.concat([{ name: s, opt:false, type:null, value:null }]),
                      fun = { ret:null, expr: { expr:makeFunctionBody(e2), pos:p }, args:args, params:[] };
                    ED.EFunction(null, fun);
                  default: throw "Ident Expected for " + e1;
                }
                default: throw "Ident Expected for " + varName;
            
              } else throw "Ident Expected for " + e1;
              
            default: throw "Ident Expected for " + e1;
            };
          else 
            shortCutLambda(expr);
        
        default: 
          shortCutLambda(expr);
          
      };
    
    return { expr:def, pos: p };
  }
  
  static public function varFromString(varDef:String, pos:Position) 
  {
    if (varDef.indexOf(":") == -1) throw "Unexpected";
    var parts = varDef.split(":");
    return {
      name : parts[0],
      type : switch (Context.parse("{var test:" + parts[1] + ";}", pos).expr) {
        case EBlock(exprs):
          switch(exprs[0].expr) {
            case EVars(vars): vars[0].type;
            default: null;
          }
        default: null;
      }
    }
  }
  
  public static function makeFunctionBody (e:Expr) {
    return switch (e.expr) {
      case ED.EBlock(_): e.expr;
      default: EReturn(e);
    }
  }
  
  
}